package com.gitee.wsl.compose.ui.widget

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.TileMode
import androidx.compose.ui.unit.DpSize
import com.gitee.wsl.common.ui.ext.color.hexString
import com.gitee.wsl.compose.modifier.draw.drawChecker
import com.gitee.wsl.compose.ui.theme.GradientAngle
import com.gitee.wsl.compose.ui.theme.GradientOffset

enum class GradientType {
    Linear, Radial, Sweep
}

@Composable
fun rememberGradientColorState(
    color: Color = Color.Unspecified,
    size: DpSize = DpSize.Zero
): GradientColorState {

    val density = LocalDensity.current

    return remember {

        val sizePx = if (size == DpSize.Zero) {
            Size.Zero
        } else {
            with(density) {
                Size(
                    size.width.toPx(),
                    size.height.toPx()
                )
            }
        }
        GradientColorState(color, sizePx)
    }
}

/**
 * Gradient and color state for setting and getting gradient color
 * with [gradientType] such as Linear, Radial or Sweep, [tileMode]s, and [colorStops].
 * * Linear gradient uses [gradientOffset] to set offset or angle.
 * * Radial gradient uses [centerFriction] and [radiusFriction]
 */
class GradientColorState internal constructor(initialColor: Color, size: Size) {

    var size by mutableStateOf(size)
    var color: Color = initialColor

    val hexString: String
        get() = color.hexString


    val brush: Brush
        get() {
            val colorStops = if (colorStops.size == 1) {
                listOf(colorStops.first(), colorStops.first()).toTypedArray()
            } else {
                colorStops.toTypedArray()
            }

            val brush = when (gradientType) {
                GradientType.Linear -> Brush.linearGradient(
                    colorStops = colorStops,
                    start = gradientOffset.start,
                    end = gradientOffset.end,
                    tileMode = tileMode
                )
                GradientType.Radial -> Brush.radialGradient(
                    colorStops = colorStops,
                    center = Offset(
                        x = size.width * centerFriction.x,
                        y = size.height * centerFriction.y
                    ),
                    radius = ((size.width.coerceAtLeast(size.height)) / 2 * radiusFriction)
                        .coerceAtLeast(0.01f),
                    tileMode = tileMode
                )
                GradientType.Sweep -> Brush.sweepGradient(
                    colorStops = colorStops,
                    center = Offset(
                        x = size.width * centerFriction.x,
                        y = size.height * centerFriction.y
                    ),
                )
            }
            return brush
        }

    /*val brushColor: BrushColor
        get() {
            return BrushColor(color = color, brush = brush)
        }*/

    var gradientType: GradientType by mutableStateOf(GradientType.Linear)

    var colorStops = mutableStateListOf(
        0.0f to Color.Red,
        0.3f to Color.Green,
        1.0f to Color.Blue,
    )

    var tileMode by mutableStateOf(TileMode.Clamp)
    var gradientOffset by mutableStateOf(GradientOffset(GradientAngle.CW0))
    var centerFriction by mutableStateOf(Offset(.5f, .5f))
    var radiusFriction by mutableStateOf(.5f)
}


@Composable
fun BrushDisplay(
    modifier: Modifier = Modifier,
    gradientColorState: GradientColorState
) {
    // Display Brush
    BoxWithConstraints(
        modifier = modifier
            .fillMaxWidth()
            .height(80.dp),
        contentAlignment = Alignment.Center
    ) {

        val size = gradientColorState.size
        val contentWidth = size.width.coerceAtLeast(2f)
        val contentHeight = size.height.coerceAtLeast(1f)
        val contentAspectRatio = contentWidth / contentHeight

        var boxHeight: Float = constraints.maxHeight.toFloat()
        var boxWidth: Float = boxHeight * contentAspectRatio

        if (boxWidth > constraints.maxWidth) {
            boxWidth = constraints.maxWidth.toFloat()
            boxHeight = (boxWidth / contentAspectRatio)

        }

        val gradientType = gradientColorState.gradientType
        val colorStops = gradientColorState.colorStops
        val gradientOffset = gradientColorState.gradientOffset
        val tileMode = gradientColorState.tileMode
        val centerFriction = gradientColorState.centerFriction
        val radiusFriction = gradientColorState.radiusFriction

        val center = Offset(
            boxWidth * centerFriction.x,
            boxHeight * centerFriction.y
        )

        val boxWidthInDp: Dp
        val boxHeightInDp: Dp
        with(LocalDensity.current) {
            boxWidthInDp = boxWidth.toDp()
            boxHeightInDp = boxHeight.toDp()
        }

        val radius = (boxHeight * radiusFriction).coerceAtLeast(0.01f)

        val brush = when (gradientType) {
            GradientType.Linear -> {
                if (colorStops.size == 1) {
                    val brushColor = colorStops.first().second
                    Brush.linearGradient(listOf(brushColor, brushColor))
                } else {
                    Brush.linearGradient(
                        colorStops = colorStops.toTypedArray(),
                        start = gradientOffset.start,
                        end = gradientOffset.end,
                        tileMode = tileMode
                    )
                }
            }
            GradientType.Radial -> {
                Brush.radialGradient(
                    colorStops = colorStops.toTypedArray(),
                    center = center,
                    radius = radius,
                    tileMode = tileMode
                )
            }

            GradientType.Sweep -> {
                Brush.sweepGradient(
                    colorStops = colorStops.toTypedArray(),
                    center = center
                )
            }
        }

        Box(
            modifier = Modifier
                .height(boxHeightInDp)
                .width(boxWidthInDp)
                .drawChecker(RoundedCornerShape(5.dp))
                .background(brush, RoundedCornerShape(5.dp))
        )
    }
}